package com.winit.schedule.task.tracker;

import java.util.List;
import java.util.Properties;

import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.StringUtils;

import com.winit.schedule.core.conf.Configuration;
import com.winit.schedule.core.task.annotation.ScheduleTask;
import com.winit.schedule.task.tracker.conf.TaskConfiguration;
import com.winit.schedule.task.tracker.quartz.QuartzScheduleListener;
import com.winit.schedule.task.tracker.quartz.QuartzTriggerListener;
import com.winit.schedule.task.tracker.schema.UScheduleClassPathBeanDefinitionScanner;

public class TaskTrackerFactoryBean implements InitializingBean, ApplicationContextAware, DisposableBean,
        BeanDefinitionRegistryPostProcessor, FactoryBean<TaskTracker>, ApplicationListener<ContextRefreshedEvent> {

    private ApplicationContext applicationContext;

    private List<Scheduler>    quartzSchedulers;

    private TaskTracker        taskTracker = new TaskTracker(); ;

    /**
     * 任务包路径
     */
    private String[]           scanPackages;

    /**
     * 通过配置文件加载配置项
     */
    private String             configLocation;

    /**
     * 通过属性注入的方式加载配置参数，同配置文件二选一;如果同时存在，属性注入优先级高于配置文件
     */
    private Properties         configProps;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public TaskTracker getObject() throws Exception {
        return taskTracker;
    }

    @Override
    public Class<TaskTracker> getObjectType() {
        return TaskTracker.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (!StringUtils.isEmpty(this.configLocation)) {
            Configuration.addClassPathResource(this.configLocation);
        }
        Configuration conf = new TaskConfiguration();
        conf.addOrReplaceConfig(configProps);
        taskTracker.setApplicationContext(applicationContext);
        taskTracker.setScanPackages(this.scanPackages);
        taskTracker.setQuartzSchedulers(quartzSchedulers);
        taskTracker.init(conf);
    }

    public void setScanPackage(String scanPackage) {
        this.scanPackages = StringUtils.tokenizeToStringArray(scanPackage, ",", true, true);
    }

    @Override
    public void destroy() throws Exception {
        taskTracker.stop();
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // TODO Auto-generated method stub

    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        if (null != scanPackages) {
            UScheduleClassPathBeanDefinitionScanner scanner = new UScheduleClassPathBeanDefinitionScanner(registry,
                true);
            AnnotationTypeFilter AnnotationTypeFilter = new AnnotationTypeFilter(ScheduleTask.class);
            scanner.addIncludeFilter(AnnotationTypeFilter);
            scanner.doScan(this.scanPackages);
        }
        taskTracker.start();
    }

    public String getConfigLocation() {
        return configLocation;
    }

    public void setConfigLocation(String configLocation) {
        this.configLocation = configLocation;
    }

    public Properties getConfigProps() {
        return configProps;
    }

    public void setConfigProps(Properties configProps) {
        this.configProps = configProps;
    }

    public void setQuartzSchedulers(List<Scheduler> quartzSchedulers) throws SchedulerException {
        this.quartzSchedulers = quartzSchedulers;
        if (null != quartzSchedulers) {
            for (Scheduler scheduler : quartzSchedulers) {
                scheduler.getListenerManager().addSchedulerListener(new QuartzScheduleListener(taskTracker, scheduler));
                scheduler.getListenerManager().addTriggerListener(new QuartzTriggerListener(taskTracker));
            }
        }
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if(event.getApplicationContext().getParent() == null) {
            taskTracker.start();
        }
    }
}
